/**
 * MemberUtilities.java
 * @author: liuex
 * 2008-1-13 ����10:38:08
 *
 * note: 
 */
package org.lex.reflect.browsing.util;

import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;

import javax.swing.tree.DefaultMutableTreeNode;

public class ClassHelper {
	public static DefaultMutableTreeNode createInheritanceRootNode(Class<?> clazz) {
		DefaultMutableTreeNode root = null;
		DefaultMutableTreeNode parent = null;
		do {
			parent = new DefaultMutableTreeNode(clazz);
			if (root != null)
				parent.add(root);
			root = parent;
		} while ((clazz = clazz.getSuperclass()) != null);
		return root;
	}

	public static DefaultMutableTreeNode createSuperTypeRootNode(Class<?> clazz) {
		DefaultMutableTreeNode root = new DefaultMutableTreeNode(clazz);
		Class<?> parent = clazz.getSuperclass();
		if (null != parent)
			root.add(createSuperTypeRootNode(parent));
		Class<?>[] inferfaces = clazz.getInterfaces();
		for (Class<?> i : inferfaces)
			root.add(createSuperTypeRootNode(i));
		return root;
	}

	public static Method[] getPublicDeclaredMethods(Class<?> clazz) {
		Method[] methods = clazz.getDeclaredMethods();
		if (null == methods || 0 == methods.length)
			return methods;
		ArrayList<Method> list = new ArrayList<Method>();
		for (Method m : methods)
			if (Modifier.isPublic(m.getModifiers()))
				list.add(m);
		return list.toArray(new Method[0]);
	}

	public static String getSimpleTypeName(Class<?> type) {
		return getTypeName(type, true);
	}

	public static String getTypeName(Class<?> type) {
		return getTypeName(type, false);
	}

	public static String getTypeName(Class<?> type, boolean simpleName) {
		if (type.isArray()) {
			try {
				Class<?> clazz = type;
				int dimensions = 0;
				while (clazz.isArray()) {
					dimensions++;
					clazz = clazz.getComponentType();
				}
				StringBuilder buf = new StringBuilder();
				buf.append(getNoneArrayTypeName(clazz, simpleName));
				for (int i = 0; i < dimensions; i++)
					buf.append("[]");
				return buf.toString();
			} catch (Throwable e) { /* FALLTHRU */
			}
		}
		return getNoneArrayTypeName(type, simpleName);
	}

	private static String getNoneArrayTypeName(Class<?> clazz, boolean simpleName) {
		StringBuilder buf = new StringBuilder();
		if (true == simpleName)
			buf.append(clazz.getSimpleName());
		else
			buf.append(clazz.getName());
		buf.append(getGenericTypes(clazz));
		return buf.toString();
	}

	private static String getGenericTypes(GenericDeclaration gd) {
		TypeVariable<?>[] types = gd.getTypeParameters();
		if (null == types || 0 == types.length)
			return "";
		StringBuilder buf = new StringBuilder(" <");
		TypeVariable<?> type = null;
		for (int i = 0; i < types.length; i++) {
			type = types[i];
			buf.append(type.getName());
			if (i < types.length - 1)
				buf.append(", ");
		}
		buf.append("> ");
		return buf.toString();
	}

	/**
	 * @param name
	 *            full name of a class
	 * @return the class or null
	 */
	public static Class<?> getClass(String name) {
		Class<?> c = null;
		try {
			ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
			if (contextClassLoader != null)
				c = Class.forName(name, true, contextClassLoader);
		} catch (Exception e) {
			e.printStackTrace();
		}
		// try plain class forName()
		if (c == null)
			try {
				c = Class.forName(name);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		return c;
	}

	public static String getGenericName(Method method) {
		try {
			StringBuilder buf = new StringBuilder();
			Type[] typeparms = method.getTypeParameters();
			if (typeparms.length > 0) {
				boolean first = true;
				buf.append("<");
				for (Type typeparm : typeparms) {
					if (!first)
						buf.append(",");
					if (typeparm instanceof Class)
						buf.append(((Class<?>) typeparm).getSimpleName());
					else
						buf.append(typeparm.toString());
					first = false;
				}
				buf.append("> ");
			}
			buf.append(method.getName() + "(");
			Type[] params = method.getGenericParameterTypes();
			for (int i = 0; i < params.length; i++) {
				if (params[i] instanceof Class)
					buf.append(getSimpleTypeName((Class<?>) params[i]));
				else {
					buf.append(params[i].toString());
				}
				if (i < (params.length - 1))
					buf.append(", ");
			}
			buf.append(")");
			Type[] exceptions = method.getGenericExceptionTypes();
			if (exceptions.length > 0) {
				buf.append(" throws ");
				for (int i = 0; i < exceptions.length; i++) {
					if (exceptions[i] instanceof Class)
						buf.append(ClassHelper.getSimpleTypeName((Class<?>) exceptions[i]));
					else
						buf.append(exceptions[i].toString());
					if (i < (exceptions.length - 1))
						buf.append(",");
				}
			}
			return buf.toString();
		} catch (Exception e) {
			return "<" + e + ">";
		}
	}
}
